using System;
using System.Collections;
using System.Xml;
using System.Reflection;

using Deepcode.Flux.Core.Services.XML;
using Deepcode.Flux.Core.Extensions.Codons;

namespace Deepcode.Flux.Core.Extensions
{
	/// <summary>
	/// AddIn represents a single extension to the Flux system. An Addin consists
	/// of an XML definition file that constructs nodes in the addin tree.
	/// </summary>
	public class Addin
	{
		#region Private Addin definition fields
		private string	name = "";
		private string	description = "";
		private string	author = "";
		private string	copyright = "";
		private string	version = "";
		private Guid	guid = Guid.Empty;
		private bool	checkupdatesatlogin = false;
		private string	installupdatescript = "";

		private bool	iscoreaddin = false;
		#endregion
		#region Addin definition properties
		/// <summary>
		/// Get the name of the addin. Name is a mandatory attribute within the addin definition file
		/// </summary>
		public string Name{ get{ return name; }}
		/// <summary>
		/// Get the description of the addin. Description is an optional attribute and could be empty
		/// </summary>
		public string Description{ get{ return description; }}
		/// <summary>
		/// Get the author of the addin. Author is an optional attribute and could be empty
		/// </summary>
		public string Author{ get{ return author; }}
		/// <summary>
		/// Get the copyright notice from the addin. Could be empty as this is an optional attribute.
		/// </summary>
		public string Copyright{ get{ return copyright; }}
		/// <summary>
		/// Get the version string from the addin. Could be empty if this is an non updatable addin
		/// </summary>
		public string Version{ get{ return version; }}
		/// <summary>
		/// Get the unique Guid for the addin. Could be empty if this is a non updatable addin.
		/// </summary>
		public Guid GUID{ get{ return guid; }}
		/// <summary>
		/// Get whether or not the addin is checked for updates being installed at admin startup.
		/// </summary>
		public bool CheckUpdatesAtLogin{ get{ return checkupdatesatlogin; }}
		/// <summary>
		/// Get the update script url to update versions of this addin. Could
		/// be empty, in which case, the addin is not updatable.
		/// </summary>
		public string InstallUpdateScript{ get{ return installupdatescript; }}
		/// <summary>
		/// Returns true if this addin is part of the Flux core
		/// </summary>
		public bool IsCoreAddin{ get{ return this.iscoreaddin; }}
		#endregion
		/// <summary>
		/// Creates a new non-core addin
		/// </summary>
		public Addin()
		{
			this.iscoreaddin = false;
		}
		/// <summary>
		/// Creates a new addin, optionally tagged as a core
		/// </summary>
		/// <param name="isCore"></param>
		public Addin( bool isCore )
		{
			this.iscoreaddin = isCore;
		}
		/// <summary>
		/// Loads the addin from it's definition file. This has several responsibilities.
		/// 1: Open the XML file and load the addin attributes from the main document element.
		/// 2: Enumerate over the first level child tags. 
		///		If it's an extension tag, create codon objects representing
		///		the structure specified, using the AddinTree to build the appropriate
		///		object.
		/// </summary>
		/// <param name="addinFile"></param>
		public void LoadFrom( string addinFile )
		{
			// Create an XmlUtility service
			XmlUtilityService xservice = new XmlUtilityService();

			XmlDocument doc = new XmlDocument();
			doc.Load( addinFile );

			// Process the addin attributes
			name = xservice.GetSafe( doc.DocumentElement, "name", "");
			if( name == "" ) throw new Exception("Name attribute is required in addin definition " + addinFile);
			description = xservice.GetSafe( doc.DocumentElement, "description", "");
			author = xservice.GetSafe( doc.DocumentElement, "author", "");
			copyright = xservice.GetSafe( doc.DocumentElement, "copyright", "");
			version = xservice.GetSafe( doc.DocumentElement, "version", "");
			guid = xservice.GetSafe( doc.DocumentElement, "guid", Guid.Empty );
			checkupdatesatlogin = xservice.GetSafe( doc.DocumentElement, "checkupdatesatlogin", false );
			installupdatescript = xservice.GetSafe( doc.DocumentElement, "installupdatescript", "");

			// Enumerate the child nodes
			foreach( object o in doc.DocumentElement.ChildNodes )
			{
				if( o is XmlElement )
				{
					XmlElement ox = (XmlElement) o;
					switch( ox.Name )
					{
						case "Extension":
							AddExtension( ox );
							break;
						case "ClearTree":
							ClearTree( ox );
							break;
					}
				}
			}
		}
		/// <summary>
		/// Clears part of the add in tree
		/// </summary>
		/// <param name="clearNode"></param>
		private void ClearTree( XmlElement clearNode )
		{
			XmlUtilityService xservice = new XmlUtilityService();
			string path = xservice.GetSafe( clearNode, "path", null );
			if( path == null ) return;

			AddinTree.Tree.ClearPath( path );
		}
		/// <summary>
		/// Loads the extension node specified and any children
		/// </summary>
		/// <param name="extensionNode"></param>
		private void AddExtension( XmlElement extensionNode )
		{
			// Extension node must have a path
			XmlUtilityService xservice = new XmlUtilityService();
			string path = xservice.GetSafe( extensionNode, "path", null );
			if( path == null )
				throw new Exception("Extension has a missing path attribute");

			// Ask the addin tree to create and obtain the treenode path
			ArrayList extensions = new ArrayList();
			Extension e = new Extension(path);
			AddCodonsToExtension( e, extensionNode, extensions );
			extensions.Add( e );
			// Now we've done loading this extension, add it (and any others created) to the addin tree
			AddinTree.Tree.AddExtensions( extensions );
		}
		/// <summary>
		/// Loads the sub-tags for an extension and creates appropriate extension objects
		/// for adding to the addin tree when the extension has loaded.
		/// </summary>
		/// <param name="e"></param>
		/// <param name="extensionNode"></param>
		/// <param name="extensions"></param>
		private void AddCodonsToExtension( Extension e, XmlElement extensionNode, ArrayList extensions )
		{
			foreach( object o in extensionNode.ChildNodes )
			{
				if( o is XmlElement )
				{
					XmlElement ox = (XmlElement) o;
					ICodon codon = AddinTree.Tree.CreateCodon( ox );
					
					if (codon.InsertAfter == null && codon.InsertBefore == null && e.CodonCollection.Count > 0) 
						codon.InsertAfter = new string []{((ICodon) e.CodonCollection[e.CodonCollection.Count-1]).ID};

					e.CodonCollection.Add(codon);
					if( ox.ChildNodes.Count > 0 )
					{
						Extension subExtend = new Extension( e.Path + "/" + codon.ID );
						AddCodonsToExtension( subExtend, ox, extensions );
						extensions.Add( subExtend );
					}
				}
			}
		}
	}
}
